/**
 * Project:   dtu.fmes.googleloon.ui
 * File:      PaintJPanel.java
 * License: 
 *            This file is licensed under GNU General Public License version 3
 *            http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * Copyright: Bartosz Cichecki [ cichecki.bartosz@gmail.com ]
 * Date:      Nov 24, 2013
 */

package dtu.fmes.loon.ui.components;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JPanel;

import dtu.fmes.loon.model.Balloon;
import dtu.fmes.loon.model.SimulationStateHistory;
import dtu.fmes.loon.model.Vector;
import dtu.fmes.loon.model.environment.Wind;
import dtu.fmes.loon.ui.utils.HSLColor;

/**
 * @author Bartosz Cichecki
 * 
 */
public class PaintJPanel extends JPanel {

	// private static final Logger log = Logger.getLogger(PaintJPanel.class);

	private static final long serialVersionUID = -2613769855388869656L;

	private Color backgroundColor = Color.WHITE;

	// private Color aliveBalloonColor = Color.GREEN;

	private Color coverageModeSignalColor = Color.BLACK;

	private Color aliveBalloonSignalColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);

	private Color resurectableBalloonColor = Color.ORANGE;

	private Color deadBalloonColor = Color.RED;

	private double balloonSize = 10;

	private double targetPosSize = 5;

	private SimulationStateHistory simulationStateHistory;

	private Map<Shape, Balloon> shapeMapping = new HashMap<Shape, Balloon>();

	private int frame;

	private double scale;

	private boolean coverageOnlyModeEnabled;

	private Double heighestWind = null;

	private Double lowestWind = null;

	public PaintJPanel() {

	}

	/**
	 * Draw area of simulation
	 * 
	 * @param g
	 */
	private void drawArea(Graphics2D g) {
		int width = (int) (simulationStateHistory.getEnvironment().getArea().getxSize() * scale);
		int height = (int) (simulationStateHistory.getEnvironment().getArea().getySize() * scale);

		Stroke oldStroke = g.getStroke();
		Color c = g.getColor();

		Stroke newStroke = new BasicStroke((float) 2.0);
		g.setStroke(newStroke);
		g.setColor(Color.BLACK);
		g.drawRect(getX(0), getY(0), width, height);

		g.setStroke(oldStroke);
		g.setColor(c);
	}

	private void drawBalloons(Graphics2D g) {
		setUpWindHeights();

		shapeMapping.clear();

		Color orginalColor = g.getColor();

		for (Balloon b : simulationStateHistory.getStateHistory().get(frame).getBalloons()) {
			// Draw balloon
			int x = getX((int) (b.getLatitude() * scale));
			int y = getY((int) (b.getLongitude() * scale));

			Shape balloonShape = new Ellipse2D.Double(x - balloonSize / 2, y - balloonSize / 2, balloonSize, balloonSize);
			g.setColor(b.isAlive() ? getGreenGradient(b.getAltitude()) : b.isCanBeRevived() ? resurectableBalloonColor : deadBalloonColor);
			g.fill(balloonShape);
			shapeMapping.put(balloonShape, b);

			// Draw optimal positions if available
			if (b.getOptimalPos() != null && !b.getOptimalPos().isEmpty()) {
				for (Vector pos : b.getOptimalPos()) {
					// Draw optimal position
					int xOptimal = getX((int) (pos.getX() * scale));
					int yOptimal = getY((int) (pos.getY() * scale));

					Shape oPosShape = new Ellipse2D.Double(xOptimal - targetPosSize / 2, yOptimal - targetPosSize / 2, targetPosSize,
					        targetPosSize);
					g.setColor(Color.RED);
					g.fill(oPosShape);

					// Draw linking line
					g.drawLine(x, y, xOptimal, yOptimal);
				}
			}

			// Draw target position if available
			if (b.getTargetPosition() != null) {
				// Draw target position
				int xTarget = getX((int) (b.getTargetPosition().getX() * scale));
				int yTarget = getY((int) (b.getTargetPosition().getY() * scale));

				Shape targetPosShape = new Ellipse2D.Double(xTarget - targetPosSize / 2, yTarget - targetPosSize / 2, targetPosSize,
				        targetPosSize);
				g.setColor(Color.DARK_GRAY);
				g.fill(targetPosShape);

				// Draw linking line
				g.drawLine(x, y, xTarget, yTarget);
			}
		}

		g.setColor(orginalColor);
	}

	/**
	 * Draw balloon ranges
	 * 
	 * @param g
	 */
	private void drawBalloonsRanges(Graphics2D g) {
		for (Balloon b : simulationStateHistory.getStateHistory().get(frame).getBalloons()) {
			int x = getX((int) (b.getLatitude() * scale));
			int y = getY((int) (b.getLongitude() * scale));

			// Draw balloon range of alive balloons
			if (b.isAlive()) {
				int signalRadius = (int) (simulationStateHistory.getConstantBalloonValues().getCommunication().getRange() * scale);
				Shape signalCoverageShape = new Ellipse2D.Double(x - signalRadius, y - signalRadius, signalRadius * 2, signalRadius * 2);
				g.setColor(coverageOnlyModeEnabled ? coverageModeSignalColor : aliveBalloonSignalColor);
				g.fill(signalCoverageShape);
			}

		}
	}

	private Color getGreenGradient(Double balloonHeight) {
		int greenGradient = (int) (60 * (balloonHeight - lowestWind) / (heighestWind - lowestWind));

		if (greenGradient > 60) {
			greenGradient = 60;
		}
		if (greenGradient < 0) {
			greenGradient = 0;
		}
		return new HSLColor(105, 100, greenGradient + 20).getRGB();
	}

	public Map<Shape, Balloon> getShapeMapping() {
		return shapeMapping;
	}

	/**
	 * Center x coordinate for current screen
	 * 
	 * @param x
	 * @return
	 */
	private int getX(int x) {
		int newX = (int) getBounds().getCenterX();
		newX += x;
		newX -= scale * simulationStateHistory.getEnvironment().getArea().getxSize() / 2;

		return newX;
	}

	/**
	 * Flip and center y coordinate for current screen
	 * 
	 * @param y
	 * @return
	 */
	private int getY(int y) {
		int newY = (int) getBounds().getCenterY();
		newY += y;
		newY -= scale * simulationStateHistory.getEnvironment().getArea().getySize() / 2;

		return newY;
	}

	/*
	 * (non-Javadoc)
	 * @see java.awt.Container#paintComponent(java.awt.Graphics)
	 */
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		setBackground(backgroundColor);
		if (simulationStateHistory != null) {
			drawArea((Graphics2D) g);
			drawBalloonsRanges((Graphics2D) g);
			if (!coverageOnlyModeEnabled) {
				drawBalloons((Graphics2D) g);
			}
		}
	}

	public void setCoverageOnlyMode(boolean enabled) {
		coverageOnlyModeEnabled = enabled;
	}

	public void setSimulationData(SimulationStateHistory simulationStateHistory, int frame, double scale) {
		this.simulationStateHistory = simulationStateHistory;
		this.frame = frame;
		this.scale = scale;
	}

	private void setUpWindHeights() {
		if (heighestWind == null || lowestWind == null) {
			Wind[] windArray = simulationStateHistory.getEnvironment().getWindArray(new Vector(0d, 0d));
			lowestWind = windArray[0].getHeight();
			heighestWind = windArray[windArray.length - 1].getHeight();
		}
	}

}
